#pragma once
#include <iostream>
#include <memory>
#include <mutex>
#include "online_user.hpp"
#include "log.hpp"

#define SESSIONTIMEOUT 30000

enum SSTATUS
{
    UNLOGIN = 0,
    LOGIN
};

class Session
{
private:
    uint32_t _ssid;
    uint32_t _uid;
    SSTATUS _status;
    websocketServer::timer_ptr _time_ptr;

public:
    Session(uint32_t ssid, uint32_t uid)
        : _ssid(ssid), _uid(uid), _time_ptr(nullptr), _status(UNLOGIN)
    {
        Log(INFO, "Session is created, the uid is : %d, sid : %d", _uid, _ssid);
    }
    ~Session()
    {
        Log(INFO, "Session is free, the uid is : %d, sid : %d", _uid, _ssid);
    }
    void setStatus(SSTATUS stat) { _status = stat; }
    SSTATUS getStatus() { return _status; }
    void setTimePtr(const websocketServer::timer_ptr& ptr) { _time_ptr = ptr; }
    websocketServer::timer_ptr getTimePtr() { return _time_ptr; }
    uint32_t getUid() { return _uid; };
    uint32_t getSid() { return _ssid; };
};

typedef std::shared_ptr<Session> sessionPtr;

class SessionManage
{
private:
    uint32_t _next_session_id;
    websocketServer* _wsvr;
    std::mutex _mutex;
    std::unordered_map<uint32_t, sessionPtr> _ssid_to_session;
public:
    SessionManage(websocketServer* svr)
        :_next_session_id(1), _wsvr(svr)
    { }
    ~SessionManage()
    { }
    sessionPtr createSession(uint32_t uid)
    {
        sessionPtr newSession(new Session(_next_session_id, uid));
        newSession->setStatus(LOGIN);

        std::unique_lock<std::mutex> lock(_mutex);
        _ssid_to_session.insert(std::make_pair(_next_session_id, newSession));
        _next_session_id++;

        return newSession;
    }
    sessionPtr findSessionFromSid(uint32_t sid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto iter = _ssid_to_session.find(sid);
        if(iter == _ssid_to_session.end())
        {
            Log(WARNING, "not find session from Sid, the find id is : %d", sid);
            return nullptr;
        }
        return iter->second;
    }
    void appendSession(sessionPtr sptr)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _ssid_to_session.insert(std::make_pair(sptr->getSid(), sptr));
    }
    void removeSession(uint32_t sid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _ssid_to_session.erase(sid);
        Log(INFO, "sid : %d is removed", sid);
    }
    void setSessionExpireTime(uint32_t sid, int ms)
    {
        sessionPtr sPtr = findSessionFromSid(sid);
        if(sPtr == nullptr)
        {
            return;
        }

        websocketServer::timer_ptr timePtr = sPtr->getTimePtr();
        //无到期任务变为无到期任务
        if(timePtr == nullptr && ms == -1)
        {
            return;
        }
        //无到期任务设置到期任务
        else if(timePtr == nullptr && ms >= 0)
        {
            websocketServer::timer_ptr tmp = _wsvr->set_timer(ms, std::bind(&SessionManage::removeSession, this, sid));
            sPtr->setTimePtr(tmp);
        }
        //有到期任务设置永久存在
        else if(timePtr != nullptr && ms == -1)
        {
            timePtr->cancel();
            sPtr->setTimePtr(nullptr);
            _wsvr->set_timer(0, std::bind(&SessionManage::appendSession, this, sPtr));
        }
        else if(timePtr != nullptr && ms >= 0)
        {
            timePtr->cancel();
            _wsvr->set_timer(0, std::bind(&SessionManage::appendSession, this, sPtr));
            websocketServer::timer_ptr tmp = _wsvr->set_timer(ms, std::bind(&SessionManage::removeSession, this, sid));
            sPtr->setTimePtr(tmp);
        }
    }
};